#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include "tp_sched.h"
#include "tp_debug.h"

#define THREAD_C 1

struct _scheduler scheduler;

struct threads  dummyStartupThread;

sigset_t alarmset;
int read_lower;

extern void timer_wheel_init();

void init_scheduler()
{
  struct sigaction sa;

  scheduler.num_procs = 0;
  scheduler.num_runable = 0;
  scheduler.last_alloc = 0;
  scheduler.head = NULL;
  scheduler.tail = NULL;
  scheduler.current = NULL;
  memset(scheduler.sockets, 0, sizeof(scheduler.sockets));
  scheduler.run_index = 0;    /* Start with first process in the run queue */
  memset(scheduler.run_queue, 0, sizeof(scheduler.run_queue));
  scheduler.switch_time = 0;  /* Really should be current time, but I'm lazy */
  scheduler.timers.queue.head = scheduler.timers.queue.tail = NULL;
  scheduler.timers.expired_queue.head =  scheduler.timers.expired_queue.tail = NULL;
  scheduler.timers.queue.reset = 1;
  scheduler.timers.expired_queue.reset = 0;
  scheduler.tp_ephemeral_next = 5001;
  scheduler.udp_ephemeral_next = 5001;
  scheduler.service_interface_now = 0;
  scheduler.gwio_data = 0;
  scheduler.gwio = 0x0;

  sigemptyset(&alarmset);
  sigaddset (&alarmset, SIGALRM);
  sigemptyset(&sa.sa_mask);/* Catch SIGALRM */

  sa.sa_flags = 0;  /* SA_INTERRUPT; *//* Don't restart system calls... */
  sa.sa_handler = (void *) &alarm_handler;
  if (sigaction (SIGALRM, &sa, NULL) < 0) {
    LOG("Error installing SIGALM signal handler\n");
    exit(2);
  }
  timer_wheel_init();
}

struct threads *create_thread(void (*program) (void),...)
{
  static struct threads *threadp;

  // create a new thread, and place it in the process queue. 
  // New threads always go on the tail of the process queue.
  threadp = (struct threads *) malloc(sizeof(struct threads));

  threadp->pid = ++scheduler.last_alloc;
  threadp->status = Created;
  threadp->function = program;
  threadp->stack_size = 1024;
  threadp->stack = (char *) malloc(THREAD_SWAP_STACK_SIZE);
  threadp->stack = &(threadp->stack[THREAD_SWAP_STACK_SIZE - 32]);
  threadp->last_run = 0;
  threadp->last_wait = 0;
  threadp->switch_time = 0;
  threadp->avg_run = 0;
  threadp->avg_wait = 0;
  threadp->read_socks = 0;
  threadp->write_socks = 0;

  /* Now, add the new task to the tail of the circular process queue */
  if (!scheduler.head){
    scheduler.head = threadp;
    threadp->prev_thread = threadp;
  } else  {
    scheduler.tail->next_thread = threadp;
    threadp->prev_thread = scheduler.tail;
  }
  scheduler.tail = threadp;
  scheduler.tail->next_thread = scheduler.head;
  scheduler.head->prev_thread = scheduler.tail;
  scheduler.num_procs++;
  scheduler.num_runable++;
  return (threadp);
}

void start_threads()
{
  dummyStartupThread.next_thread = scheduler.run_queue[0];
  scheduler.current = &dummyStartupThread;
}

void show_scheduler(void)
{
  printf ("num_procs              %d\n", scheduler.num_procs);
  printf ("num_runable            %d\n", scheduler.num_runable);
  printf ("last_alloc             %d\n", scheduler.last_alloc);
  printf ("stack_growth           %d\n", scheduler.stack_growth);
  printf ("run_index              %d\n", scheduler.run_index);
  printf ("tp_ephemeral_next      %d\n", scheduler.tp_ephemeral_next);

  printf ("udp_ephemeral_next     %d\n", scheduler.udp_ephemeral_next);
  printf ("service_interface_now  %d\n", scheduler.service_interface_now);
  printf ("gwio_data             %d\n", scheduler.gwio_data);
  printf ("tp_ephemeral_next      %d\n", scheduler.tp_ephemeral_next);
  printf ("gwio                  %p\n", scheduler.gwio);

  printf ("switch_time %u\n", scheduler.switch_time);
  int i;
  for (i = 0; i < MAX_SCPS_SOCKET; i++){
    if (scheduler.sockets[i].ptr != NULL) {
      printf ("socket %d read %d write %d\n", i, scheduler.sockets[i].read, scheduler.sockets[i].write);
    }
  }
}
